﻿// From: http://www.codeproject.com/KB/WPF/PieChartDataBinding.aspx
// With small changes from Patrick Boos

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Media.Animation;
using ScrumTable.UI.View.WinTouch.UserControls.PieChart.Shapes;

namespace ScrumTable.UI.View.WinTouch.UserControls.PieChart.PieChart
{
    /// <summary>
    /// Renders a bound dataset as a pie chart
    /// </summary>
    public partial class PiePlotter 
    {
        private CollectionView _collectionView;
        #region dependency properties

        /// <summary>
        /// The property of the bound object that will be plotted
        /// </summary>
        public String PlottedProperty
        {
            get { return PieChartLayout.GetPlottedProperty(this); }
            set { PieChartLayout.SetPlottedProperty(this, value); }
        }

        /// <summary>
        /// The property of the bound object that will be used as color
        /// </summary>
        public String ColorProperty
        {
            get { return PieChartLayout.GetColorProperty(this); }
            set { PieChartLayout.SetColorProperty(this, value); }
        }

        /// <summary>
        /// A class which selects a color based on the item being rendered.
        /// </summary>
        public IColorSelector ColorSelector
        {
            get { return PieChartLayout.GetColorSelector(this); }
            set { PieChartLayout.SetColorSelector(this, value); }
        }


        /// <summary>
        /// The size of the hole in the centre of circle (as a percentage)
        /// </summary>
        public double HoleSize
        {
            get { return (double)GetValue(HoleSizeProperty); }
            set
            {
                SetValue(HoleSizeProperty, value);
                ConstructPiePieces();
            }
        }

        public static readonly DependencyProperty HoleSizeProperty =
            DependencyProperty.Register("HoleSize", typeof(double), typeof(PiePlotter), new UIPropertyMetadata(0.0));

       
        #endregion


        /// <summary>
        /// A list which contains the current piece pieces, where the piece index
        /// is the same as the index of the item within the collection view which 
        /// it represents.
        /// </summary>
        private List<PiePiece> piePieces = new List<PiePiece>();

        public PiePlotter()
        {
            if (!DesignerProperties.GetIsInDesignMode(this))
            {
                // register any dependency property change handlers
                /*
                DependencyPropertyDescriptor dpd =
                    DependencyPropertyDescriptor.FromProperty(PieChartLayout.PlottedPropertyProperty,
                                                              typeof (PiePlotter));
                dpd.AddValueChanged(this, PlottedPropertyChanged);
*/
                InitializeComponent();

                this.DataContextChanged += new DependencyPropertyChangedEventHandler(DataContextChangedHandler);
            }
        }


        ~PiePlotter()
        {
            Dispose(false);
        }


        /// <summary>
        /// Implement dispose interface in order to support runtime support.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);

            // Use SupressFinalize in case a subclass
            // of this type implements a finalizer.
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes the managed and unmanaged resources.
        /// </summary>
        /// <param name="disposing">True to dispose the managed resources, otherwise false.</param>
        protected virtual void Dispose(bool disposing)
        {

            if (_collectionView == null)
            {
                return;
            }
            foreach (object item in _collectionView)
            {
                if (item is INotifyPropertyChanged)
                {
                    INotifyPropertyChanged observable = (INotifyPropertyChanged)item;
                    observable.PropertyChanged -= new PropertyChangedEventHandler(ItemPropertyChanged);
                }
            }

            

            _collectionView.CurrentChanged -= new EventHandler(CollectionViewCurrentChanged);
            _collectionView.CurrentChanging -= new CurrentChangingEventHandler(CollectionViewCurrentChanging);
        }

        #region property change handlers

        /// <summary>
        /// Handle changes in the datacontext. When a change occurs handlers are registered for events which
        /// occur when the collection changes or any items within teh collection change.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DataContextChangedHandler(object sender, DependencyPropertyChangedEventArgs e)
        {
            // handle the events that occur when the bound collection changes
            if (this.DataContext is INotifyCollectionChanged)
            {
              //  INotifyCollectionChanged observable = (INotifyCollectionChanged)this.DataContext;
         //       observable.CollectionChanged += new NotifyCollectionChangedEventHandler(BoundCollectionChanged);
            }

            // handle the selection change events
            if (_collectionView != null)
            {
                _collectionView = (CollectionView) CollectionViewSource.GetDefaultView(this.DataContext);

                _collectionView.CurrentChanged += new EventHandler(CollectionViewCurrentChanged);
                _collectionView.CurrentChanging += new CurrentChangingEventHandler(CollectionViewCurrentChanging);
            }
            ConstructPiePieces();
            ObserveBoundCollectionChanges();
        }

        /// <summary>
        /// Handles changes to the PlottedProperty property.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PlottedPropertyChanged(object sender, EventArgs e)
        {
            ConstructPiePieces();
        }

        #endregion

        #region event handlers

        /// <summary>
        /// Handles the MouseUp event from the individual Pie Pieces
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void PiePieceMouseUp(object sender, MouseButtonEventArgs e)
        {
            SelectPiePiece(sender);
        }

        /// <summary>
        /// Handles the Tap Gesture event from the individual Pie Pieces
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PiePieceTapGesture(object sender, TouchEventArgs e)
        {
            SelectPiePiece(sender);
        }

        private void SelectPiePiece(object sender)
        {
            CollectionView collectionView = (CollectionView)CollectionViewSource.GetDefaultView(this.DataContext);
            if (collectionView == null)
                return;

            PiePiece piece = sender as PiePiece;
            if (piece == null)
                return;

            // select the item which this pie piece represents
            int index = (int)piece.Tag;
            collectionView.MoveCurrentToPosition(index);
        }

        /// <summary>
        /// Handles the event which occurs when the selected item is about to change
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void CollectionViewCurrentChanging(object sender, CurrentChangingEventArgs e)
        {
            CollectionView collectionView = (CollectionView)sender;

            if (collectionView != null && collectionView.CurrentPosition >= 0 && collectionView.CurrentPosition <= piePieces.Count)
            {
                PiePiece piece = piePieces[collectionView.CurrentPosition];

                DoubleAnimation a = new DoubleAnimation();
                a.To = 0;
                a.Duration = new Duration(TimeSpan.FromMilliseconds(200));

                piece.BeginAnimation(PiePiece.PushOutProperty, a);
            }
        }

        /// <summary>
        /// Handles the event which occurs when the selected item has changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void CollectionViewCurrentChanged(object sender, EventArgs e)
        {
            CollectionView collectionView = (CollectionView)sender;

            if (collectionView != null && collectionView.CurrentPosition >= 0 && collectionView.CurrentPosition <= piePieces.Count)
            {
                PiePiece piece = piePieces[collectionView.CurrentPosition];

                DoubleAnimation a = new DoubleAnimation();
                a.To = 10;
                a.Duration = new Duration(TimeSpan.FromMilliseconds(200));

                piece.BeginAnimation(PiePiece.PushOutProperty, a);
            }

            
        }

        /// <summary>
        /// Handles events which are raised when the bound collection changes (i.e. items added/removed)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BoundCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            ConstructPiePieces();
            ObserveBoundCollectionChanges();
        }

        /// <summary>
        /// Iterates over the items inthe bound collection, adding handlers for PropertyChanged events
        /// </summary>
        private void ObserveBoundCollectionChanges()
        {
            CollectionView myCollectionView = (CollectionView)CollectionViewSource.GetDefaultView(this.DataContext);
            if (myCollectionView ==null)               
            {
                return;
            }

            foreach(object item in myCollectionView)
            {
                if (item is INotifyPropertyChanged)
                {
                    INotifyPropertyChanged observable = (INotifyPropertyChanged)item;
                    observable.PropertyChanged += new PropertyChangedEventHandler(ItemPropertyChanged);
                }
            }
        }

        
        /// <summary>
        /// Handles events which occur when the properties of bound items change.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // if the property which this pie chart represents has changed, re-construct the pie
            if (e.PropertyName.Equals(PlottedProperty))
            {
                ConstructPiePieces();
            }
        }

        #endregion

        private double GetPlottedPropertyValue(object item)
        {
            PropertyDescriptorCollection filterPropDesc = TypeDescriptor.GetProperties(item);
            object itemValue = filterPropDesc[PlottedProperty].GetValue(item);

            return (double)itemValue;
        }

        private SolidColorBrush GetColorPropertyValue(object item)
        {
            PropertyDescriptorCollection filterPropDesc = TypeDescriptor.GetProperties(item);
            object itemValue = filterPropDesc[ColorProperty].GetValue(item);

            return (SolidColorBrush)itemValue;
        }

        /// <summary>
        /// Constructs pie pieces and adds them to the visual tree for this control's canvas
        /// </summary>
        private void ConstructPiePieces()
        {
            CollectionView myCollectionView = (CollectionView)CollectionViewSource.GetDefaultView(this.DataContext);
            if (myCollectionView == null)
                return;

            double halfWidth = this.Width / 2;
            double innerRadius = halfWidth * HoleSize;            

            // compute the total for the property which is being plotted
            double total = 0;
            foreach (Object item in myCollectionView)
            {
                total += GetPlottedPropertyValue(item);
            }
            
            // add the pie pieces
            canvas.Children.Clear();
            piePieces.Clear();
                        
            double accumulativeAngle=0;
            foreach (Object item in myCollectionView)
            {
                bool selectedItem = item == myCollectionView.CurrentItem;

                double wedgeAngle = GetPlottedPropertyValue(item) * 360 / total;

                Brush fillBrush;
                if(!ColorProperty.Equals(""))
                    fillBrush = GetColorPropertyValue(item);
                else
                    fillBrush = ColorSelector != null ? ColorSelector.SelectBrush(item, myCollectionView.IndexOf(item)) : Brushes.Black;
                

                PiePiece piece = new PiePiece()
                                     {
                                         Radius = halfWidth,
                                         InnerRadius = innerRadius,
                                         CentreX = halfWidth,
                                         CentreY = halfWidth,
                                         PushOut = (selectedItem ? 10.0 : 0),
                                         WedgeAngle = wedgeAngle,
                                         PieceValue = GetPlottedPropertyValue(item),
                                         RotationAngle = accumulativeAngle,
                                         Fill = fillBrush,
                                         Stroke = new SolidColorBrush(Colors.Black),
                                         // record the index of the item which this pie slice represents
                                         Tag = myCollectionView.IndexOf(item),
                                         ToolTip = new ToolTip()
                                     };

                piece.ToolTipOpening += PiePieceToolTipOpening;
                piece.ContactTapGesture += PiePieceTapGesture;
                piece.MouseUp += PiePieceMouseUp;

                piePieces.Add(piece);
                canvas.Children.Insert(0, piece);

                accumulativeAngle += wedgeAngle;
            }
        }

        /// <summary>
        /// Handles the event which occurs just before a pie piece tooltip opens
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void PiePieceToolTipOpening(object sender, ToolTipEventArgs e)
        {
            PiePiece piece = (PiePiece)sender;

            CollectionView collectionView = (CollectionView)CollectionViewSource.GetDefaultView(this.DataContext);
            if (collectionView == null)
                return;
                       
            // select the item which this pie piece represents
            int index = (int)piece.Tag;
            if (piece.ToolTip != null)
            {
                ToolTip tip = (ToolTip)piece.ToolTip;
                tip.DataContext = collectionView.GetItemAt(index);
            }         
        }

    }
}